## -*- coding: utf-8 -*-

#   Copyright (C) 2007, 2008 Yves Junqueira (yves@cetico.org) 
#
#    This file is part of GeekDNS.
#
#    GeekDNS is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    GeekDNS is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with GeekDNS.  If not, see <http://www.gnu.org/licenses/>.


import random
import logging
from datetime import datetime
import time

from sqlobject import *
from turbogears.database import PackageHub
from turbogears import identity

hub = PackageHub('geekroot')
__connection__ = hub

log = logging.getLogger("geekroot.model")


def now():
    return int(time.time())


class PhysicalServer(SQLObject):
    """Servidor fisico. 
    Todo "Server" eh vinculado a um PhysicalServer
    """
    Domain = StringCol(length=255,alternateID=True)
    memory_mb = IntCol()
    transfer_gb = IntCol()
    disk_gb = IntCol()
    setup_cost_usd = DecimalCol(size=10, precision=2)
    monthly_cost_usd = DecimalCol(size=10, precision=2)
    admin_notes = UnicodeCol(default='')
    main_ip = StringCol(length=15,default='')
    setup_date = DateTimeCol(default=datetime.now, notNone=True)
    rimu_oid=StringCol(length=255,default='')

class ServerPlan(SQLObject):
    """Plano ou modelo de servidores
    Escolhido no momento do pedido. Define as características
    padrões de cada servidor.
    """
    planname = StringCol(length=16)
    memory_mb = IntCol()
    transfer_gb = IntCol()
    disk_gb = IntCol()
    price_real = DecimalCol(size=10, precision=2) 

def find_plan(name=None, id=None):
    """Encontra plano procurando pelo nome ou id
    """
    if not (name or id):
        return False
    if name: 
        try:
            plan = \
            ServerPlan.select(ServerPlan.q.planname==name)[0]
        except IndexError:
            return False
    elif id:
        try:
            plan = \
            ServerPlan.select(ServerPlan.q.id==id)[0]
        except IndexError:
            return False
    return plan

class Server(SQLObject):
    """Servidores unitários
    1 VPS = 1 Servidor = 1 Pedido
    """
    domain = StringCol(length=255, alternateID=True)
    order_date = DateTimeCol(default=datetime.now, notNone=True)
    main_ip = StringCol(length=15, default='')
    notes = UnicodeCol(default='')
    admin_notes = UnicodeCol(default='')
    price_real = DecimalCol(size=10, precision=2)
    memory_mb = IntCol()
    disk_gb = IntCol()
    transfer_gb = IntCol()
    active = BoolCol(default=False)
    rimuid = IntCol(default=None)

    invoices = MultipleJoin('ServerInvoice')
    plan = ForeignKey('ServerPlan', notNone=True)
    panel = ForeignKey('Panel', default=1)
    physicalserver = ForeignKey('PhysicalServer', default=1)
    owner = ForeignKey('User', notNone=True)
    distro = ForeignKey('Distro', notNone=True)


def invoice_is_unique(pubid):
    invoice_count = ServerInvoice.select(ServerInvoice.q.pubid==pubid).count()
    return not(invoice_count)

def rand_invoice_id(): 
    _pubid = random.randint(10000000,99999999)
    while (not(invoice_is_unique(_pubid))):
        _pubid = random.randint(10000000,99999999)
    return _pubid

class ServerInvoice(SQLObject):
    """Cobranças
    """

    amount_real = DecimalCol(size=10, precision=2, notNone=True)
    invoice_date = DateTimeCol(default=datetime.now, notNone=True)
    description = UnicodeCol(default='')
    paid = BoolCol(default=False)
    pubid = IntCol(default=rand_invoice_id,alternateID=True)
    
    server = ForeignKey('Server', notNone=True)

class Distro(SQLObject):
    """Distribuições disponíveis
    """
    name = StringCol(length=255,alternateID=True)
    enabled = BoolCol(default=True)

class Panel(SQLObject):
    """Paineis de controle disponíveis
    Ex: Webmin, nenhum
    """
    name = StringCol(length=255,alternateID=True)
    enabled = BoolCol(default=True)

# identity models.
class Visit(SQLObject):
    """
    A visit to your site
    """
    class sqlmeta:
        table = 'visit'

    visit_key = StringCol(length=40, alternateID=True,
                          alternateMethodName='by_visit_key')
    created = DateTimeCol(default=datetime.now)
    expiry = DateTimeCol()

    def lookup_visit(cls, visit_key):
        try:
            return cls.by_visit_key(visit_key)
        except SQLObjectNotFound:
            return None
    lookup_visit = classmethod(lookup_visit)

class VisitIdentity(SQLObject):
    """
    A Visit that is link to a User object
    """
    visit_key = StringCol(length=40, alternateID=True,
                          alternateMethodName='by_visit_key')
    user_id = IntCol()

class Group(SQLObject):
    """
    An ultra-simple group definition.
    """
    # names like "Group", "Order" and "User" are reserved words in SQL
    # so we set the name to something safe for SQL
    class sqlmeta:
        table = 'tg_group'

    group_name = UnicodeCol(length=16, alternateID=True,
                            alternateMethodName='by_group_name')
    full_name = UnicodeCol(length=255)
    created = DateTimeCol(default=datetime.now)

    # collection of all users belonging to this group
    users = RelatedJoin('User', intermediateTable='user_group',
                        joinColumn='group_id', otherColumn='user_id')

    # collection of all permissions for this group
    permissions = RelatedJoin('Permission', joinColumn='group_id',
                              intermediateTable='group_permission',
                              otherColumn='permission_id')

class User(SQLObject):
    """
    Reasonably basic User definition.
    Probably would want additional attributes.
    """
    
    # names like "Group", "Order" and "User" are reserved words in SQL
    # so we set the name to something safe for SQL
    class sqlmeta:
        table = 'tg_user'

    email_address = UnicodeCol(length=255, alternateID=True,
                               alternateMethodName='by_user_name')
    full_name = UnicodeCol(length=255)
    street_address = UnicodeCol(length=255)
    city = UnicodeCol(length=255)
    state = UnicodeCol(length=5)
    zipcode = UnicodeCol(length=15)
    country = UnicodeCol(length=255)
    phone1 = UnicodeCol(length=30)
    # these are optional
    phone2 = UnicodeCol(length=30)
    mobilephone = UnicodeCol(length=30)
    homepage = UnicodeCol(length=255)

    password = UnicodeCol(length=40)
    created = DateTimeCol(default=datetime.now)

    # groups this user belongs to
    groups = RelatedJoin('Group', intermediateTable='user_group',
                         joinColumn='user_id', otherColumn='group_id')

    def _get_permissions(self):
        perms = set()
        for g in self.groups:
            perms = perms | set(g.permissions)
        return perms

    def _set_password(self, cleartext_password):
        "Runs cleartext_password through the hash algorithm before saving."
        password_hash = identity.encrypt_password(cleartext_password)
        self._SO_set_password(password_hash)

    def set_password_raw(self, password):
        "Saves the password as-is to the database."
        self._SO_set_password(password)

class Permission(SQLObject):
    """
    A relationship that determines what each Group can do
    """
    permission_name = UnicodeCol(length=16, alternateID=True,
                                 alternateMethodName='by_permission_name')
    description = UnicodeCol(length=255)

    groups = RelatedJoin('Group',
                         intermediateTable='group_permission',
                         joinColumn='permission_id',
                         otherColumn='group_id')

class DNSDomain(SQLObject):
    """A PowerDNS domain
    """
    class sqlmeta:
        table = 'domains' # since this is expected by powerdns
        createSQL = { 'mysql' : 'ALTER TABLE domains ENGINE InnoDB' }

    name = StringCol(length=255, alternateID=True)
    master = StringCol(length=20,default='')
    last_check = IntCol(default=now)
    type = StringCol(length=6)
    notified_serial= IntCol(default=0)
    account = StringCol(length=40, default='')
    owner = ForeignKey('User', notNone=True)

    def update_serial(self):
        soa = DNSRecord.select(AND(DNSRecord.q.type=='SOA',
        DNSRecord.q.domainID==self.id))[0]
        log.debug("Found domain SOA: %s" % soa.content)
        soa._soa_update_serial()

class DNSRecord(SQLObject):
    """PowerDNS DNS records
    """
    class sqlmeta:
        table = 'records' # since this is expected by powerdns
        createSQL = { 'mysql' : 'ALTER TABLE records ENGINE InnoDB' }

    domain = ForeignKey('DNSDomain')
    # can be == domain, or a subdomain
    name = StringCol(length=255)
    type = StringCol(length=6,default='')
    content = StringCol(length=255,default='')
    ttl = IntCol(default=3600)
    prio = IntCol(default=0)
    change_date = IntCol(default=now,notNone=True)

    rec_name = DatabaseIndex(name)
    nametype_index = DatabaseIndex(name, type)

    def _soa_update_serial(self):
        """If the current record is a SOA, updates it
        using the format YYYYMMDDxx
        """
        assert self.type == 'SOA'
        serial = datetime.now().strftime("%Y%m%d01")
        cur_content=self.content
        log.debug("Current SOA content: %s" % cur_content)
        soa=self.content.split(' ')
        cur_serial=soa[2]
        log.debug("Current SOA Serial: %s" % cur_serial)
        if int(serial) <= int(cur_serial):
            # there another update today
            new_serial = int(cur_serial) + 1
            serial = str(new_serial)
        log.debug("New SOA Serial: %s" % serial)
        soa[2] = serial
        self.content = " ".join(soa)

class DNSSuperMaster(SQLObject):
    """PowerDNS Supermasters
    """
    class sqlmeta:
        table = 'supermasters' # since this is expected by powerdns
        createSQL = { 'mysql' : 'ALTER TABLE supermasters ENGINE InnoDB' }
    nameserver = StringCol(length=255)
    account = StringCol(length=40,default='')


def server_domain_is_unique(domain):
    "Return True if that server domain is not yet in the database."
    domain_count = Server.select(Server.q.domain==domain).count()
    return not(domain_count)


def dns_domain_is_unique(domain):
    "Return True if that dns domain is not yet in the database."
    domain_count = DNSDomain.select(DNSDomain.q.name==domain).count()
    return not(domain_count)


def so_row_to_dict(object):
    """Transforma uma row em dicionario, facilitando uso.
    Utilizado juntamente com '.widgets.dict_html_table()'.
    """
    format = {}
    for col in object.sqlmeta.columnList:
        format[col.name] = getattr(object, col.name)
    return format

